package com.example.demo.jdbc.custom;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Slf4j
@NoArgsConstructor
@AllArgsConstructor
public class Save extends AbstractMethod {
    private static final String SQL_SCRIPT = "<script>\nINSERT INTO %s %s VALUES %s ON DUPLICATE KEY UPDATE %s\n</script>";
    
    private static final String SQL_METHOD = "save";
    
    @Setter
    @Accessors(chain = true)
    private Predicate<TableFieldInfo> predicate;
    
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        KeyGenerator keyGenerator = new NoKeyGenerator();
        String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(false) +
                this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);
        String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;
        String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(ENTITY_DOT, false) +
                this.filterTableFieldInfo(fieldList, predicate, item -> {
                    String template = String.format("%s != null", ENTITY_DOT + item.getProperty());
                    Class<?> propertyType = item.getPropertyType();
                    if (propertyType.getSimpleName().equals("String")) {
                        template = String.format("%s != null and %s != ''", ENTITY_DOT + item.getProperty(), ENTITY_DOT + item.getProperty());
                    }
                    return SqlScriptUtils.convertChoose(template, item.getInsertSqlProperty(ENTITY_DOT), item.getColumn() + COMMA);
                }, EMPTY);
        
        String valuesScript = SqlScriptUtils.convertTrim(insertSqlProperty, LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
        
        String updateValues = genUpdateValues(tableInfo);
        String sql = String.format(SQL_SCRIPT, tableInfo.getTableName(), columnScript, valuesScript, updateValues);
        
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        String keyProperty = null;
        String keyColumn = null;
        if (tableInfo.havePK()) {
            if (tableInfo.getIdType() == IdType.AUTO) {
                /* 自增主键 */
                keyGenerator = new Jdbc3KeyGenerator();
                keyProperty = tableInfo.getKeyProperty();
                keyColumn = tableInfo.getKeyColumn();
            } else {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(SQL_METHOD, tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }
        
        return this.addInsertMappedStatement(mapperClass, modelClass, SQL_METHOD, sqlSource, keyGenerator, keyProperty, keyColumn);
    }
    
    private String genUpdateValues(TableInfo tableInfo) {
        String template = "%s = values(%s),";
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        String collect = fieldList.stream().map(item -> {
            String valuesScript = String.format(template, item.getColumn(), item.getColumn());
            String s = SqlScriptUtils.convertIf(valuesScript, String.format("%s != null", ENTITY_DOT + item.getProperty()), true);
            return s;
        }).collect(Collectors.joining());
        return SqlScriptUtils.convertTrim(collect, null, null, null, COMMA);
    }
}
